#include "duktape.h"
#include "log.h"
#include "utils.h"
#include "os.h"
#include "audio.h"
#include "on_msg.h"
static duk_ret_t tone(duk_context *ctx) {
    if(!duk_is_string(ctx,0)){
        return DUK_RET_TYPE_ERROR;
    }
    
    const char* tone_string = "01234567890abcd*#";
    const char* tone_txt = duk_to_string(ctx,0);
    for(int i = 0;i<16;i++){
        if(tone_string[i] == tone_txt[0]){
            platform_tone(i,1000);
            break;
        }
    }
    return 0;
}
//设置音频通道
static duk_ret_t setChannel(duk_context *ctx){
    int chan = duk_get_int(ctx,0);
    platform_audio_set_channel(chan);
    return 0;
}

static duk_ret_t setSpeakerVol(duk_context *ctx){
    int vol = duk_get_int(ctx,0);
    platform_audio_set_speaker_vol(vol);
    return 0;
}

static void* record_stream_to_task = 0;

typedef struct RecordStreamData_t{
    Msg msg;
    void* data;
    int dataLen;
}RecordStreamData;
static void rec_cb(int status,void* data,int size){
    if(record_stream_to_task){
        RecordStreamData* pMsg = (RecordStreamData*)malloc(sizeof(RecordStreamData));
        if(!pMsg){
            ERROR("record stream data callback alloc failed");
            return ;
        }
        pMsg->msg.id = MSG_RECORD_STREAM_DATA_CB;
        pMsg->msg.args = 0;
        pMsg->data = malloc(size);
        if(!pMsg->data){
            ERROR("record stream data callback alloc pcm data failed");
            free(pMsg);
            return ;
        }
        memcpy(pMsg->data,data,size);
        pMsg->dataLen = size;
        platform_send_msg(record_stream_to_task,pMsg);
    }else{
        ERROR("record_stream_to_task is null");
    }
}

typedef struct RecordEndData_t{
    Msg msg;
    int status;
}RecordEndData;
static void rec_result_cb(int result){
    if(record_stream_to_task){
        RecordEndData* pMsg = (RecordEndData*)malloc(sizeof(RecordEndData));
        if(!pMsg){
            ERROR("record stream end callback alloc failed");
            return ;
        }
        pMsg->msg.id = MSG_RECORD_STREAM_END_CB;
        pMsg->msg.args = 0;
        pMsg->status = result;
        platform_send_msg(record_stream_to_task,pMsg);
    }else{
        ERROR("record_stream_to_task is null");
    }
}

static int on_record_stream_callback_msg(duk_context* ctx,RecordStreamData* pMsg){
    // TRACE("on_record_stream_callback_msg");
    do{
        vget_head_stash_object(ctx,"record_stream_data_callback");//[...,data_callback]
        if(!duk_is_function(ctx,-1)){
            duk_pop(ctx);
            ERROR("no record stream data callback");
            break;
        }
        void* pJsBuffer = duk_push_fixed_buffer(ctx,pMsg->dataLen);//[...,data_callback,databuffer]
        if(!pJsBuffer){
            duk_pop(ctx);//[...]
            ERROR("record stream data malloc js buffer failed.");
            break;
        }
        memcpy(pJsBuffer,pMsg->data,pMsg->dataLen);
        duk_pcall(ctx,1);//[...]
        duk_pop(ctx);
    }while(0);
    free(pMsg->data);
    return 0;
}
static int on_record_stream_end_callback_msg(duk_context* ctx,RecordEndData* pMsg){
    do{
        vget_head_stash_object(ctx,"record_stream_end_callback");//[...,end_callback]
        if(!duk_is_function(ctx,-1)){
            duk_pop(ctx);
            ERROR("no record stream end callback");
            break;
        }
        duk_push_int(ctx,pMsg->status);//[...,end_callback,status]
        duk_pcall(ctx,1);//[...]
        duk_pop(ctx);
        vdel_head_stash_object(ctx,"record_stream_data_callback");
        vdel_head_stash_object(ctx,"record_stream_end_callback");
        record_stream_to_task = 0;
    }while(0);    
    return 0;
}
//[stream_callaback,end_callback]
static duk_ret_t record_pcm_stream(duk_context *ctx){
    if(duk_is_function(ctx,0)){
        vsave_head_stash_object(ctx,0,"record_stream_data_callback");
    }
    if(duk_is_function(ctx,1)){
        vsave_head_stash_object(ctx,1,"record_stream_end_callback");
    }
    record_stream_to_task = platform_current_task();
    int ret = platform_record_pcm_stream(rec_cb,rec_result_cb);
    duk_push_int(ctx,ret);
    return 1;
}
static duk_ret_t record_stop(duk_context *ctx){

    platform_record_stop();
    return 0;
}


static int on_play_pcm_file_end_msg(duk_context* ctx,Msg* pMsg){
    int result = pMsg->args;
    get_head_stash_object(ctx,"play_pcm_file_cb");//[play_pcm_file_cb]
    del_head_stash_object(ctx,"play_pcm_file_cb");//[play_pcm_file_cb]
    if(duk_is_function(ctx,-1)){
        duk_push_int(ctx,result);
        duk_pcall(ctx,1);//[ret]
        duk_pop(ctx);//[]
    }else{
        duk_pop(ctx);//[]
    }
    
    return 0;
}

static int on_play_pcm_stream_end_msg(duk_context* ctx,Msg* pMsg){
    int result = pMsg->args;
    get_head_stash_object(ctx,"play_pcm_s_cb");//[play_pcm_s_cb]
    if(duk_is_function(ctx,-1)){
        duk_push_int(ctx,result);//[play_pcm_s_cb,result]
        duk_pcall(ctx,1);//[ret]
        duk_pop(ctx);//[]
    }else{
        duk_pop(ctx);//[]
    }
    del_head_stash_object(ctx,"play_pcm_s_cb");//
        
    return 0;
}
static int on_record_pcm_file_msg(duk_context* ctx,Msg* pMsg){
    int result = pMsg->args;
    get_head_stash_object(ctx,"record_pcm_file_cb");//[record_pcm_file_cb]
    del_head_stash_object(ctx,"record_pcm_file_cb");//[record_pcm_file_cb]
    if(duk_is_function(ctx,-1)){
        duk_push_int(ctx,result);
        duk_pcall(ctx,1);//[ret]
        duk_pop(ctx);//[]
    }else{
        duk_pop(ctx);//[]
    }
            
    return 0;
}


static void* play_pcmfile_task = 0;
static play_pcmfile_cb(int result){
    // debugf("play_pcmfile_cb task:%p",play_pcmfile_task);
    platform_play_stop();
    if(play_pcmfile_task){
        Msg* pMsg = (Msg*)malloc(sizeof(Msg));
        if(pMsg){
            pMsg->id = MSG_PLAY_PCM_FILE_CB;
            pMsg->args = (void*)result;
            platform_send_msg(play_pcmfile_task,pMsg);
        }
    }
    play_pcmfile_task = 0;
}
//[filename,end_callback]
static duk_ret_t play_pcm_file(duk_context *ctx){
    if(!duk_is_string(ctx,0)){
        debugf("arg0 is not string");
        duk_error(ctx,DUK_ERR_SYNTAX_ERROR,"arg2 is not string");
    }
    if(!duk_is_function(ctx,1)){
        debugf("arg2 is not function");
        duk_error(ctx,DUK_ERR_SYNTAX_ERROR,"arg2 is not function");
    }    
    if(play_pcmfile_task){
        duk_error(ctx,DUK_ERR_ERROR,"playing");
    }
    char* filename = duk_get_string(ctx,0);
    play_pcmfile_task = platform_current_task();
    save_head_stash_object(ctx,"play_pcm_file_cb",1);    
    int ret = platform_play_pcm_file(filename,play_pcmfile_cb);
    duk_push_int(ctx,ret);
    return 1;
}

void* record_pcm_file_task = 0;
static void record_pcm_file_cb(int result){
    platform_record_stop();
    if(record_pcm_file_task){
        Msg* pMsg = (Msg*)malloc(sizeof(Msg));
        if(pMsg){
            pMsg->id = MSG_RECORD_PCM_FILE_CB;
            pMsg->args = (void*)result;
            platform_send_msg(record_pcm_file_task,pMsg);
        }
    }
    record_pcm_file_task = 0;
}
//[filename,millseconds,end_callback]
static duk_ret_t record_pcm_file(duk_context *ctx){
    if(!duk_is_string(ctx,0)){
        debugf("arg0 is not string");
        duk_error(ctx,DUK_ERR_SYNTAX_ERROR,"arg2 is not string");
    }
    if(!duk_is_number(ctx,1)){
        debugf("arg0 is not int");
        duk_error(ctx,DUK_ERR_SYNTAX_ERROR,"arg2 is not int");
    }
    if(!duk_is_function(ctx,2)){
        debugf("arg2 is not function");
        duk_error(ctx,DUK_ERR_SYNTAX_ERROR,"arg2 is not function");
    }
    char* filename = duk_get_string(ctx,0);//default 1s
    record_pcm_file_task = platform_current_task();
    int ms = duk_get_int_default(ctx,1,0);
    save_head_stash_object(ctx,"record_pcm_file_cb",2);
    int ret = platform_record_pcm_file(filename,ms,record_pcm_file_cb);
    duk_push_int(ctx,ret);
    return 1;
}
static duk_ret_t play_stop(duk_context *ctx){
    platform_play_stop();
    return 0;
}
static void play_pcm_stream_cb(int result){   
    if(play_pcmfile_task){
        Msg* pMsg = (Msg*)malloc(sizeof(Msg));
        if(pMsg){
            pMsg->id = MSG_PLAY_PCM_STREAM_CB;
            pMsg->args = (void*)result;
            platform_send_msg(play_pcmfile_task,pMsg);
        }
    }
}

static void play_pcm_stream_end(int result){
    platform_play_stop();
    if(play_pcmfile_task){
        Msg* pMsg = (Msg*)malloc(sizeof(Msg));
        if(pMsg){
            pMsg->id = MSG_PLAY_PCM_STREAM_CB;
            pMsg->args = (void*)result;
            platform_send_msg(play_pcmfile_task,pMsg);
        }
    }    
}
static play_pcm_stream_no_js_cb(int result){
    platform_play_stop();
}
//[buffer,callback]
static duk_ret_t play_pcm_strem(duk_context *ctx){
    if(!duk_is_buffer_data(ctx,0)){
        duk_error(ctx,DUK_ERR_ERROR,"play_pcm_strem arg0 is not buffer");
    }

    duk_size_t pcmSize = 0;
    void* pcmBuffer = duk_get_buffer_data(ctx,0,&pcmSize);
    if(pcmSize <= 0){
        duk_error(ctx,DUK_RET_RANGE_ERROR,"arg0 buffer size <= 0");
    }
    play_pcmfile_task = platform_current_task();
    PLAY_END_CB cb = play_pcm_stream_no_js_cb;
    if(duk_is_function(ctx,1)){
        save_head_stash_object(ctx,"play_pcm_s_cb",1);
        cb = play_pcm_stream_cb;
    }
    int ret = 0;
    const int PLAY_SIZE = 960;
    for(ret = 0;ret<pcmSize;){
        int s = platform_play_pcm_stream(pcmBuffer+ret,PLAY_SIZE,cb);
        if(s == 0){
            break;
        }
        ret += s;
    }
    

    duk_push_int(ctx,ret);
    return 1;
}
static duk_function_list_entry module_funcs[] = {
    {"tone",tone,1},
    {"setChannel",setChannel,1},
    {"setSpeakerVol",setSpeakerVol,1},
    {"record_pcm_stream",record_pcm_stream,2},
    {"record_stop",record_stop,0},
    {"play_pcm_file",play_pcm_file,2},
    {"play_stop",play_stop,0},
    {"play_pcm_stream",play_pcm_strem,2},
    {"record_pcm_file",record_pcm_file,3},
    {0/*name*/,0/*c function*/,0 /*nargs*/}
};

static duk_number_list_entry module_numbers[] = {
    {"OPENAT_AUDIOHAL_ITF_RECEIVER",0},
    {"OPENAT_AUDIOHAL_ITF_EARPIECE",1},
    {"OPENAT_AUDIOHAL_ITF_LOUDSPEAKER",2},
    {"OPENAT_AUD_CHANNEL_DUMMY_AUX_HANDSET",3},
    {0,0},
};

int audio_init(duk_context* ctx){
    platform_audio_init();
    register_msg_handler(MSG_RECORD_STREAM_DATA_CB,on_record_stream_callback_msg);
    register_msg_handler(MSG_RECORD_STREAM_END_CB,on_record_stream_end_callback_msg);
    register_msg_handler(MSG_PLAY_PCM_FILE_CB,on_play_pcm_file_end_msg);
    register_msg_handler(MSG_RECORD_PCM_FILE_CB,on_record_pcm_file_msg);
    register_msg_handler(MSG_PLAY_PCM_STREAM_CB,on_play_pcm_stream_end_msg);
    register_module_funcs(ctx,"iot.audio",module_funcs);
    register_module_numbers(ctx,"iot.audio",module_numbers);
    return 0;
}
